package com.tang.framework.config.mybatisPlus.interceptor;

import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;

import com.tang.framework.annotation.DataAuthority;
import com.tang.framework.core.ContextHold;
import com.tang.framework.core.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.schema.Table;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author tang jun liang
 * @Date 2023/9/3 19:31
 */
@Component
@Slf4j
public class CustomizeMultiDataPermissionHandler implements MultiDataPermissionHandler {


    @Override
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
        // 这里获取权限注解 有两种方法
        // 一个是通执行方法栈找到最近的一个   本类 提供 getDataAuthority获取
        // 一个是通过aop拦截方法 把注解放在ContextHold
        DataAuthority dataAuthority = getDataAuthority();
        //如果没有权限注解代表不需要数据权限
        if (Objects.isNull(dataAuthority)) {
            return null;
        }

        LoginUser loginUser = ContextHold.getLoginUser();
        //不是登录过后的请求也不需要 直接返回
        if (Objects.isNull(loginUser)) {
            return null;
        }

        return getFinalExpression(where, dataAuthority, loginUser, table);
    }


    /**
     * 获取数据权限的表达式
     *
     * @param dataAuthority
     * @param loginUser
     * @param table
     * @return
     */
    private Expression getFinalExpression(Expression where, DataAuthority dataAuthority, LoginUser loginUser, Table table) {

        String tableName = table.getName();
        if (Objects.nonNull(table.getAlias())) {
            tableName = table.getAlias().getName();
        }
        Expression authorityDataExpression = null;
        if (dataAuthority.value().equals(DataAuthority.Type.NORMAL)) {
            authorityDataExpression = processNormalDataAuthority(dataAuthority, loginUser, tableName);
        }
        if (Objects.isNull(authorityDataExpression)) {
            return null;
        }
        AndExpression deptExpression = new AndExpression();
        deptExpression.withUseOperator(false);
        deptExpression.withLeftExpression(where);
        deptExpression.setRightExpression(authorityDataExpression);

        return deptExpression;
    }



    /**
     * 正常数据权限
     *
     * @param dataAuthority
     * @param loginUser
     * @return
     */
    private Expression processNormalDataAuthority(DataAuthority dataAuthority, LoginUser loginUser, String tableName) {
        //全部的权限 就不需要过滤了
        if (Objects.requireNonNull(loginUser).getTenantDataAuthor()) {
            return null;
        }
        //部门权限
        String dataAuthoritySql = processDeptAuthority(tableName, loginUser) +
                //用户权限
                processUserAuthority(tableName, loginUser);
        return new StringValue(dataAuthoritySql);
    }

    /**
     * 通过方法执行栈找到第一个数据权限注解
     *
     * @param
     * @return
     */
    private DataAuthority getDataAuthority() {
        StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();

        Class<?> clazz;
        for (StackTraceElement stackTrace : stackTraces) {
            try {
                if (stackTrace.getClassName().startsWith("jdk.internal.reflect.GeneratedMethodAccessor")) {
                    continue;
                }
                clazz = Class.forName(stackTrace.getClassName());
                Method[] declaredMethods = clazz.getDeclaredMethods();
                Method preMethod = null;
                for (Method declaredMethod : declaredMethods) {
                    if (declaredMethod.getName().equals(stackTrace.getMethodName())) {
                        preMethod = declaredMethod;
                        break;
                    }
                }
                assert Objects.nonNull(preMethod);
                if (preMethod.isAnnotationPresent(DataAuthority.class)) {
                    return preMethod.getAnnotation(DataAuthority.class);
                }
            } catch (Exception e) {
                log.error("获取权限注解失败", e);
            }
        }

        return null;
    }

    /**
     * 部门权限
     *
     * @param tableName
     * @param loginUser
     * @return
     */
    private String processDeptAuthority(String tableName, LoginUser loginUser) {
        StringBuilder deptAuthoritySql = new StringBuilder();
        if (loginUser.getDeptDataAuthor()) {
            deptAuthoritySql.append(tableName).append(".").append("create_dept_id");
            //如果部门id 不为空 要加上这些部门的权限
            if (!loginUser.getEndowDeptIdList().isEmpty()) {
                List<Long> allEndowDeptId = loginUser.getEndowDeptIdList();
                allEndowDeptId.add(loginUser.getDeptId());
                String deptJoin = allEndowDeptId.stream().map(String::valueOf).collect(Collectors.joining(","));
                deptAuthoritySql.append(" in (").append(deptJoin).append(")");
            } else {
                Long deptId = 0L;
                //用户没有部门 那就直接等于0
                if (Objects.nonNull(loginUser.getDeptId())) {
                    deptId = loginUser.getDeptId();
                }
                //如果没有授权的部门id
                deptAuthoritySql.append(" = ").append(deptId);
            }
        }
        return deptAuthoritySql.toString();
    }


    /**
     * 用户权限
     *
     * @param tableName
     * @param loginUser
     * @return
     */
    private String processUserAuthority(String tableName, LoginUser loginUser) {
        StringBuilder userAuthoritySql = new StringBuilder();
        if (loginUser.getUserDataAuthor()) {
            userAuthoritySql.append(" and ");
            userAuthoritySql.append(tableName).append(".").append("create_user_id");
            //如果用户id 不为空 要加上这些部门的权限
            if (!loginUser.getEndowUserIdList().isEmpty()) {
                List<Long> allEndowDeptId = loginUser.getEndowUserIdList();
                allEndowDeptId.add(loginUser.getUserId());
                String deptJoin = allEndowDeptId.stream().map(String::valueOf).collect(Collectors.joining(","));
                userAuthoritySql.append(" in (").append(deptJoin).append(")");
            } else {
                //如果没有授权的用户id
                userAuthoritySql.append(" = ").append(loginUser.getUserId());
            }
        }
        return userAuthoritySql.toString();
    }
}
